#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/of_address.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/timer.h>

#define TIMER_NAME  "timer"
#define TIMER_COUNT  1

#define OPEN_CMD       _IO(0xEF, 1)  // 打开命令
#define CLOSE_CMD      _IO(0xEF, 2)  // 关闭命令
#define SETPERIOD_CMD  _IOW(0xEF, 3, int)  // 设置周期

struct timer_dev {
    dev_t devid;
    int major;
    int minor;

    struct cdev cdev;
    struct class *class;
    struct device *device;
    struct device_node *nd;

    int led_gpio; /* key所使用的GPIO编号 */
    int timeperiod; /* 定时周期,单位为ms */
    struct timer_list timer; /* 定义一个定时器 */
    spinlock_t lock; /* 定义自旋锁 */
};
struct timer_dev timer;

static int led_init(struct timer_dev *dev)
{
    int ret = 0;
    // 获取根节点
    dev->nd = of_find_node_by_path("/gpioled");
    if(dev->nd == NULL)
    {
        ret = -EINVAL;
        goto fail_findnd;
    }

    dev->led_gpio = of_get_named_gpio(dev->nd, "led-gpios", 0);
    if(dev->led_gpio < 0)
    {
        ret = -EINVAL;
        goto fail_gpio;
    }

    ret = gpio_request(dev->led_gpio, "led");
    if(ret < 0)
    {
        ret = -EBUSY;
        printk("IO %d Can't request!\r\n", dev->led_gpio);
        goto fail_request;
    }

    ret = gpio_direction_output(dev->led_gpio, 0);
    if(ret < 0)
    {
        ret = -EINVAL;
        goto fail_gpioset;
    }

    return 0;

fail_gpioset:
    gpio_free(dev->led_gpio);
fail_request:
fail_gpio:
fail_findnd:
    return ret;
}

int timer_open (struct inode *node, struct file *filep)
{
    printk(" open !\r\n");
    filep->private_data = &timer;
    return 0;
}

ssize_t timer_read (struct file *filp, char __user *buf, size_t size, loff_t *offset)
{
    int ret = 0;

    return ret;
}

int timer_release (struct inode *node, struct file *filep)
{
    struct timer_dev *dev = filep->private_data;
    dev = NULL;
    printk(" release !\r\n");

    return 0;
}

long timer_ioctl (struct file *filep, unsigned int cmd, unsigned long arg)
{
    int ret = 0;
    int value = 0;
    struct timer_dev *dev = filep->private_data;

    switch(cmd) {
        case OPEN_CMD:
            mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod));
            break;
        case CLOSE_CMD:
            del_timer_sync(&dev->timer);
            break;
        case SETPERIOD_CMD:
            ret = __copy_from_user(&value, (int *)arg, sizeof(int));
            if(ret < 0){
                return -EFAULT;
            }
            dev->timeperiod = value;
            mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod));
            break;
        default:break;
    }
    return ret;
}

static struct file_operations timer_ops = {
    .owner = THIS_MODULE,
    .open = timer_open,
    .unlocked_ioctl = timer_ioctl,
    .release = timer_release,
};

/* 定时器处理函数 */
static void timer_function(unsigned long arg)
{
    struct timer_dev *dev = (struct timer_dev *)arg;
    static int status = 0;
    status = !status;

    gpio_set_value(dev->led_gpio, status);
    mod_timer(&timer.timer, jiffies + msecs_to_jiffies(dev->timeperiod));
}

static int __init timer_init(void)
{
    int ret = 0;
    printk(" init !\r\n");

    /* 1、注册设备号 */
    timer.major = 0;
    if(timer.major){
        ret = register_chrdev_region(timer.devid, TIMER_COUNT, TIMER_NAME);
    }
    else{
        ret = alloc_chrdev_region(&timer.devid, 0, TIMER_COUNT, TIMER_NAME);
        timer.major = MAJOR(timer.devid);
        timer.minor = MINOR(timer.devid);
    }
    if(ret < 0){
        goto fail_devid;
    }
    printk("timer.major = %d, timer.minor = %d\r\n", timer.major, timer.minor);

    cdev_init(&timer.cdev, &timer_ops);
    ret = cdev_add(&timer.cdev, timer.devid, TIMER_COUNT);
    if(ret < 0){
        goto fail_cdev;
    }

    timer.class = class_create(THIS_MODULE, TIMER_NAME);
    if(IS_ERR(timer.class)){
        ret = PTR_ERR(timer.class);
        goto fail_class;
    }

    timer.device = device_create(timer.class, NULL, timer.devid, NULL,TIMER_NAME);
    if(IS_ERR(timer.device)){
        ret = PTR_ERR(timer.device);
        goto fail_device;
    }

    ret = led_init(&timer);
    if(ret < 0)
    {
        goto fail_ledinit;
    }
    // 初始化timer，设置定时器处理函数,还未设置周期，所以不会激活定时器
    init_timer(&timer.timer);
    timer.timeperiod = 500;
    timer.timer.function = timer_function;
    timer.timer.expires = jiffies + msecs_to_jiffies(timer.timeperiod);
    timer.timer.data = (unsigned long)&timer;
    add_timer(&timer.timer);  //添加到系统

    return 0;

fail_ledinit:
    device_destroy(timer.class, timer.devid);
fail_device:
    class_destroy(timer.class);
fail_class:
    cdev_del(&timer.cdev);
fail_cdev:
    unregister_chrdev_region(timer.devid, TIMER_COUNT);
fail_devid:
    return ret;
}

static void __exit timer_exit(void)
{
    gpio_set_value(timer.led_gpio, 1);
    gpio_free(timer.led_gpio);
    del_timer(&timer.timer); //删除定时器

    device_destroy(timer.class, timer.devid);
    class_destroy(timer.class);
    cdev_del(&timer.cdev);
    unregister_chrdev_region(timer.devid, TIMER_COUNT);

    printk(" exit !\r\n");
}


module_init(timer_init);
module_exit(timer_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("george <g_yubo@163.com>");